//! 板级数据定义试试
//!
//! 板子的 bsp 定义, 包括 cpu 数量定义, 内存范围定义, 控制台定义, 中断控制器定义等.

use proc_macro::TokenStream;
use proc_macro2::{Ident, Span};
use quote::quote;
use syn::{ItemStatic, Type, parse, parse_macro_input, token::Comma};

#[derive(Debug)]
struct BspDataArgs {
    console: Ident,
    irqchip: Ident,
    clock: Ident,
}

impl parse::Parse for BspDataArgs {
    fn parse(input: parse::ParseStream) -> syn::Result<Self> {
        let console = input.parse()?;
        input.parse::<Comma>()?;
        let irqchip = input.parse()?;
        input.parse::<Comma>()?;
        let clock = input.parse()?;
        Ok(BspDataArgs { console, irqchip, clock })
    }
}

/// 定义板级数据
///
/// # Panics
/// 当定义属性不符合`(console, irqchip)`时将会 panic.
#[proc_macro_attribute]
pub fn define_bsp_data(attr: TokenStream, input: TokenStream) -> TokenStream {
    let f = parse_macro_input!(input as ItemStatic);
    let at = parse_macro_input!(attr as BspDataArgs);
    let name = Ident::new(&format!("{}", f.ident), Span::call_site());

    let raw_ty = if let Type::Path(t) = f.ty.as_ref() {
        let ty2 = t.path.segments.first().unwrap().ident.clone();
        Some(Ident::new(&format!("{ty2}"), Span::call_site()))
    } else {
        None
    };
    let ty = raw_ty.unwrap();

    let console_ty = Ident::new(&format!("{}", at.console), Span::call_site());
    let irqchip_ty = Ident::new(&format!("{}", at.irqchip), Span::call_site());
    let clock_ty = Ident::new(&format!("{}", at.clock), Span::call_site());

    let asm0 = include_str!("aarch64/define.S");
    let asm1 = include_str!("aarch64/macro.S");
    let asm2 = include_str!("aarch64/head.S");
    let asm3 = include_str!("aarch64/cache.S");
    let asm4 = include_str!("aarch64/entry.S");
    let asm5 = include_str!("aarch64/clear_page.S");

    quote!(
        core::arch::global_asm!(#asm0);
        core::arch::global_asm!(#asm1);
        core::arch::global_asm!(#asm2);
        core::arch::global_asm!(#asm3);
        core::arch::global_asm!(#asm4);
        core::arch::global_asm!(#asm5);

        unsafe extern "Rust" {
            fn __start_kernel() -> !;
            fn __secondary_start_kernel() -> !;
        }

        #[unsafe(export_name = "__ffi_start_kernel")]
        #[doc(hidden)]
        pub extern "C" fn ffi_start_kernel() {
            unsafe {
                __start_kernel();
            }
        }

        #[unsafe(export_name = "__ffi_secondary_start_kernel")]
        #[doc(hidden)]
        pub extern "C" fn ffi_secondary_start_kernel() {
            unsafe {
                __secondary_start_kernel();
            }
        }

        #[unsafe(export_name = "__early_init")]
        #[doc(hidden)]
        #[inline(always)]
        pub unsafe fn early_init() {
            #name.early_init();
        }

        #[unsafe(export_name = "__late_init")]
        #[doc(hidden)]
        #[inline(always)]
        pub unsafe fn late_init() {
            #name.late_init();
        }

        #[unsafe(export_name = "__secondary_init")]
        #[doc(hidden)]
        #[inline(always)]
        pub unsafe fn secondary_init() {
            #name.secondary_init();
        }

        #f

        static __SMP_DEFINE: seminix::bsp::smp::SmpDefine = #ty::DEFINE_SMP;
        #[unsafe(export_name = "__smp_define")]
        #[doc(hidden)]
        pub fn smp_define() -> &'static seminix::bsp::smp::SmpDefine {
            &__SMP_DEFINE
        }

        static __MEMS_DEFINE: seminix::bsp::memrange::MemDefine = #ty::DEFINE_MEMS;
        #[unsafe(export_name = "__mems_define")]
        #[doc(hidden)]
        pub fn mems_define() -> &'static seminix::bsp::memrange::MemDefine {
            &__MEMS_DEFINE
        }

        static __CONSOLE_DEFINE: seminix::sync::spinlock::Spinlock<#console_ty> =
                    seminix::sync::spinlock::Spinlock::new(#ty::DEFINE_CONSOLE);
        #[unsafe(export_name = "__console_init")]
        pub fn console_init() {
            let mut lock = __CONSOLE_DEFINE.lock_irq_save();
            use seminix::bsp::console::ConsoleImpl;
            lock.init();
        }
        #[unsafe(export_name = "__console_output")]
        #[doc(hidden)]
        pub fn console_output(args: core::fmt::Arguments<'_>) {
            let mut lock = __CONSOLE_DEFINE.lock_irq_save();
            use core::fmt::Write;
            lock.write_fmt(args).unwrap();
        }

        static __IRQCHIP: #irqchip_ty = #ty::DEFINE_IRQCHIP;
        use seminix::bsp::irqchip::IrqchipImpl;
        #[unsafe(export_name = "__irqchip_name")]
        #[doc(hidden)]
        pub fn irqchip_name() -> &'static str {
            __IRQCHIP.name()
        }

        #[unsafe(export_name = "__irqchip_irqs_max")]
        #[doc(hidden)]
        pub fn irqchip_irqs_max() -> usize {
            __IRQCHIP.irqs_max()
        }

        #[unsafe(export_name = "__irqchip_irq_ack")]
        #[doc(hidden)]
        pub fn irqchip_irq_ack(hwirq: u32) {
            __IRQCHIP.irq_ack(hwirq)
        }

        #[unsafe(export_name = "__irqchip_irq_eoi")]
        #[doc(hidden)]
        pub fn irqchip_irq_eoi(hwirq: u32) {
            __IRQCHIP.irq_eoi(hwirq)
        }

        #[unsafe(export_name = "__irqchip_irq_disable")]
        #[doc(hidden)]
        pub fn irqchip_irq_disable(hwirq: u32) {
            __IRQCHIP.irq_disable(hwirq);
        }

        #[unsafe(export_name = "__irqchip_irq_enable")]
        #[doc(hidden)]
        pub fn irqchip_irq_enable(hwirq: u32) {
            __IRQCHIP.irq_enable(hwirq);
        }

        #[unsafe(export_name = "__irqchip_irq_set_affinity")]
        #[doc(hidden)]
        pub fn irqchip_irq_set_affinity(hwirq: u32, cpumask: seminix::bsp::smp::Cpumask) -> seminix::bsp::irqchip::Result<()> {
            __IRQCHIP.irq_set_affinity(hwirq, cpumask)
        }

        #[unsafe(export_name = "__irqchip_irq_set_type")]
        #[doc(hidden)]
        pub fn irqchip_irq_set_type(hwirq: u32, irq_type: seminix::bsp::irqchip::IrqType) -> seminix::bsp::irqchip::Result<()> {
            __IRQCHIP.irq_set_type(hwirq, irq_type)
        }

        #[unsafe(export_name = "__irqchip_irq_get_irqchip_state")]
        #[doc(hidden)]
        pub fn irqchip_irq_get_irqchip_state(hwirq: u32, which: seminix::bsp::irqchip::IrqchipIrqState) -> seminix::bsp::irqchip::Result<bool> {
            __IRQCHIP.irq_get_irqchip_state(hwirq, which)
        }

        #[unsafe(export_name = "__irqchip_irq_set_irqchip_state")]
        #[doc(hidden)]
        pub fn irqchip_irq_set_irqchip_state(hwirq: u32, which: seminix::bsp::irqchip::IrqchipIrqState, state: bool) -> seminix::bsp::irqchip::Result<()> {
            __IRQCHIP.irq_set_irqchip_state(hwirq, which, state)
        }

        #[unsafe(export_name = "__irqchip_ipi_send_single")]
        #[doc(hidden)]
        pub fn irqchip_ipi_send_single(hwirq: u32, cpu: usize) {
            __IRQCHIP.ipi_send_single(hwirq, cpu);
        }

        #[unsafe(export_name = "__irqchip_ipi_send_mask")]
        #[doc(hidden)]
        pub fn irqchip_ipi_send_mask(hwirq: u32, cpumask: &seminix::bsp::smp::Cpumask) {
            __IRQCHIP.ipi_send_mask(hwirq, cpumask);
        }

        #[unsafe(export_name = "__irqchip_irq_handler")]
        #[doc(hidden)]
        pub fn irqchip_irq_handler() {
            __IRQCHIP.irq_handler();
        }

        #[unsafe(export_name = "__irqchip_irq_init")]
        #[doc(hidden)]
        pub fn irqchip_irq_init() {
            __IRQCHIP.irq_init();
        }

        #[unsafe(export_name = "__irqchip_irq_init_cpu")]
        #[doc(hidden)]
        pub fn irqchip_irq_init_cpu() {
            __IRQCHIP.irq_init_cpu();
        }

        static __CLOCK_DEFINE: #clock_ty = #ty::DEFINE_CLOCK;
        use seminix::bsp::clocksource::ClockImpl;
        #[unsafe(export_name = "__clock_init")]
        #[doc(hidden)]
        pub fn clock_init() {
            __CLOCK_DEFINE.init()
        }

        #[unsafe(export_name = "__clock_init_cpu")]
        #[doc(hidden)]
        pub fn clock_init_cpu() {
            __CLOCK_DEFINE.init_cpu()
        }

        #[unsafe(export_name = "__clock_get_set_next_event")]
        #[doc(hidden)]
        pub fn get_set_next_event() -> seminix::bsp::clocksource::SetNextEventFuc {
            __CLOCK_DEFINE.get_set_next_event()
        }

        #[unsafe(export_name = "__clock_get_set_state_stoped")]
        #[doc(hidden)]
        pub fn get_set_state_stoped() -> seminix::bsp::clocksource::SetStateStopedFuc {
            __CLOCK_DEFINE.get_set_state_stoped()
        }

        #[unsafe(export_name = "__clock_rate")]
        #[doc(hidden)]
        pub fn rate() -> usize {
            __CLOCK_DEFINE.rate()
        }

        #[unsafe(export_name = "__clock_min_delta")]
        #[doc(hidden)]
        pub fn min_delta() -> u64 {
            __CLOCK_DEFINE.min_delta()
        }

        #[unsafe(export_name = "__clock_max_delta")]
        #[doc(hidden)]
        pub fn max_delta() -> u64 {
            __CLOCK_DEFINE.max_delta()
        }

        #[unsafe(export_name = "__system_tick")]
        #[doc(hidden)]
        pub fn system_tick() -> usize {
            #ty::DEFINE_SYSTEM_TICK
        }
    )
    .into()
}
